package opentracing

import (
	"context"
	"errors"
	"fmt"
	"github.com/SkyAPM/go2sky"
	"github.com/SkyAPM/go2sky/propagation"
	"github.com/SkyAPM/go2sky/reporter"
	language_agent "github.com/SkyAPM/go2sky/reporter/grpc/language-agent"
	"github.com/micro/go-micro/v2/metadata"
	"sync"
	"time"
)

const ComponentId_AI = 5500
type Tag string
const (
	TagURL             Tag = "url"
	TagStatusCode      Tag = "status_code"
	TagHTTPMethod      Tag = "http.method"
	TagGRPCMethod      Tag = "grpc.method"
	TagDBType          Tag = "db.type"
	TagDBInstance      Tag = "db.instance"
	TagDBStatement     Tag = "db.statement"
	TagDBBindVariables Tag = "db.bind_vars"
	TagMQQueue         Tag = "mq.queue"
	TagMQBroker        Tag = "mq.broker"
	TagMQTopic         Tag = "mq.topic"
)

var ErrNoTracer error = errors.New("No available tracer")
var (
	once sync.Once
	traceField string = "trace_field"
    globalTracer *OtTracer
)
type OtTracer struct{
	tracer *go2sky.Tracer
}
func (t *OtTracer) CreateEntrySpan(ctx context.Context, operationName string) (*OtSpan, error) {
	if nil != t.tracer {
		extractor := func()(string, error){
			if md, ok := metadata.FromContext(ctx); ok {
				//fmt.Println("===========extractor====================", md)
				if header, bExist := md[propagation.Header]; bExist{
					//fmt.Println("===========extractor====================", header)
					return header, nil
				}else if header, bExist := md["Sw8"]; bExist{
					return header, nil
				}
			}
			//fmt.Println("===========extractor===============no propagation.Header=====", propagation.Header)
			return "",nil
		}

		span, newCtx, err :=  t.tracer.CreateEntrySpan(ctx, operationName, extractor)
		if err != nil {
			return nil , err
		}
		otSpan := &OtSpan{
			span:span,
			newCtx: newCtx,
		}
		otSpan.span.SetComponent(ComponentId_AI)
		otSpan.span.SetSpanLayer(language_agent.SpanLayer_RPCFramework)
		return otSpan, nil
	}
	return nil, ErrNoTracer
}
//func (t *OtTracer) CreateLocalSpan(ctx context.Context, opts ...go2sky.SpanOption) (go2sky.Span, context.Context, error) {
//	if nil != t.tracer {
//		return t.CreateLocalSpan(ctx, opts...)
//	}
//	return nil, nil, ErrNoTracer
//}
func (t *OtTracer) CreateExitSpan(ctx context.Context, operationName string, peer string) (*OtSpan, error) {
	if nil != t.tracer {
		var md metadata.Metadata
		if oldMd, ok := metadata.FromContext(ctx); ok {
			md = oldMd
		}else {
			md = make(metadata.Metadata)
		}
		//ctx, span, err := StartSpanFromContext(ctx, o.ot, name)
		injector := func(value string) error{
			if 0 < len(value) {
				fmt.Println("===========injector====================", value)
				md.Set(propagation.Header, value)
				fmt.Println("===========injector====================", md)
			}
			return  nil
		}
		span, err := t.tracer.CreateExitSpan(ctx, operationName, peer, injector)
		if err != nil {
			return nil , err
		}

		newCtx := metadata.NewContext(ctx, md)
		otSpan := &OtSpan{
			span:span,
			newCtx: newCtx,
		}
		//defer gkSpan.End()

		//logger.Info("Trace Call ", name, ctx)
		otSpan.span.SetComponent(ComponentId_AI)
		otSpan.span.SetSpanLayer(language_agent.SpanLayer_RPCFramework)
		//span.Tag(go2sky.Tag(TagStatusCode), "0")
		return otSpan, nil
		//return t.tracer.CreateExitSpan(ctx, operationName, peer, injector)
	}
	return nil, ErrNoTracer
}

type OtSpan struct{
	span go2sky.Span
	newCtx context.Context
}

func(os *OtSpan) End(){
	os.span.End()
}

//设置错误Tag
func(os *OtSpan) Error(t time.Time,  opt ...string){
	os.span.Error(t , opt...)
}
//设置Tag
func (os *OtSpan) Tag(tagName string , tagValue string){
	os.span.Tag(go2sky.Tag(tagName), tagValue)
}

//从对象中获取所需Tags, 并设置到span 中
func(os *OtSpan) ExtractTraceField(msg interface{}, tagPrefix string){
	walk := func(prefix, tag string, field interface{}, kind uint) error{
		key := prefix + tag
		value := fmt.Sprintf("%v", field)
		//fmt.Println("otWrapper call", key, value)
		os.span.Tag(go2sky.Tag(key), value)
		return nil
	}

	//从req 获取 信息， 根据proto中的 trace_fileld
	reflectTags(msg, traceField, tagPrefix, walk)
}



func GlobalTracer() *OtTracer {
	once.Do(func() {
		//fmt.Println("process pid:", os.Getpid())
		globalTracer = &OtTracer{}

	})
	return globalTracer
}

func TraceBoot(oapServer string, serviceName string, instanceId string) *OtTracer{
	//只创建一次,如果tracer 已经存在，直接返回
	gOt := GlobalTracer()
	if nil != gOt.tracer{
		return gOt
	}

	var report go2sky.Reporter
	var err error
	//if grpc {
	report, err = reporter.NewGRPCReporter(oapServer)
	if err != nil {
		//log.Error("crate grpc reporter error", zap.Error(err))
		return nil
	}
	//} else {
	//	report, err = reporter.NewLogReporter()
	//	if err != nil {
	//		log.Fatalf("crate log reporter error: %v \n", err)
	//	}
	//}

	tracer, err := go2sky.NewTracer(serviceName, go2sky.WithReporter(report), go2sky.WithInstance(instanceId))
	if err != nil {
		//log.Error("crate tracer error", zap.Error(err))
		return nil
	}


	gOt.tracer = tracer
	return gOt
}

